package com.xingzhe.orders.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xingzhe.base.exception.XingZhePlusException;
import com.xingzhe.base.utils.QRCodeUtil;
import com.xingzhe.messagesdk.model.po.MqMessage;
import com.xingzhe.messagesdk.service.MqMessageService;
import com.xingzhe.orders.config.AlipayConfig;
import com.xingzhe.orders.config.PayNotifyConfig;
import com.xingzhe.orders.mapper.XcOrdersGoodsMapper;
import com.xingzhe.orders.mapper.XcOrdersMapper;
import com.xingzhe.orders.mapper.XcPayRecordMapper;
import com.xingzhe.orders.model.dto.AddOrderDto;
import com.xingzhe.orders.model.dto.PayRecordDto;
import com.xingzhe.orders.model.dto.PayStatusDto;
import com.xingzhe.orders.model.po.XcOrders;
import com.xingzhe.orders.model.po.XcOrdersGoods;
import com.xingzhe.orders.model.po.XcPayRecord;
import com.xingzhe.orders.service.OrderService;
import com.xingzhe.orders.utils.IdWorkerUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 订单服务
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    XcOrdersMapper ordersMapper;
    @Resource
    XcOrdersGoodsMapper ordersGoodsMapper;

    @Resource
    XcPayRecordMapper payRecordMapper;

    @Resource
    RabbitTemplate rabbitTemplate;

    @Resource
    MqMessageService mqMessageService;


    @Resource
    OrderServiceImpl currentProxy;

    @Value("${pay.qrcodeurl}")
    String qrcodeurl;

    @Value("${pay.alipay.APP_ID}")
    String APP_ID;
    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    String ALIPAY_PUBLIC_KEY;

    @Transactional
    @Override
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) {
        //进行幂等性判断，同一个选课记录只能有一个订单
        //添加商品订单
        //插入订单表、订单明细表
        XcOrders orders = saveXcOrders(userId, addOrderDto);

        //添加支付记录
        XcPayRecord xcPayRecord = createXcPayRecord(orders);
        Long payNo = xcPayRecord.getPayNo();//支付号

        //生成二维码
        QRCodeUtil qrCodeUtil = new QRCodeUtil();
        String url = String.format(qrcodeurl, payNo);
        String qrCode=null;
        try {
             qrCode = qrCodeUtil.createQRCode(url, 200, 200);
        } catch (IOException e) {
            XingZhePlusException.cast("生成支付二维码失败");
        }
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(xcPayRecord,payRecordDto);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    @Override
    public XcPayRecord getPayRecordByPayno(String payNo) {
        XcPayRecord xcPayRecord = payRecordMapper.selectOne(new LambdaQueryWrapper<XcPayRecord>().eq(XcPayRecord::getPayNo, payNo));
        return xcPayRecord;
    }

    @Override
    public PayRecordDto queryPayResult(String payNo) {
        //调用支付宝接口查询支付后返回结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);
        System.out.println(payStatusDto);
        //获取到支付结果就更新支付记录表和订单表的支付状态
        currentProxy.saveAliPayStatus(payStatusDto);

        //要返回最新的支付记录信息
        XcPayRecord payRecordByPayno = getPayRecordByPayno(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecordByPayno,payRecordDto);
        return payRecordDto;
    }

    /**
     * @description 保存支付宝支付结果
     * @param payStatusDto  支付结果信息
     * @return void
     * @author Mr.M
     * @date 2022/10/4 16:52
     */
    @Transactional
    @Override
    public void saveAliPayStatus(PayStatusDto payStatusDto) {
        //支付流水号
        String payNo = payStatusDto.getOut_trade_no();
        XcPayRecord payRecord = getPayRecordByPayno(payNo);
        if (payRecord == null) {
            XingZhePlusException.cast("没有该支付记录哦~");
        }

        //关联的订单号
        Long orderId = payRecord.getOrderId();
        XcOrders orders = ordersMapper.selectById(orderId);
        if (orders == null) {
            XingZhePlusException.cast("根据支付记录找不到订单");
        }

        //支付结果
        String statusFromDb = payRecord.getStatus();
        log.debug("收到支付结果:{},支付记录:{}}", payStatusDto.toString(),payRecord.toString());
        //查看状态码是否为支付成功
        if (statusFromDb.equals("601002")) {
            //是，则直接返回，无需再支付
            return;
        }
        //需要支付，则看一下支付宝回调的结果是否为成功，是才继续将数据库的支付记录表和订单表的数据更新
        String trade_status = payStatusDto.getTrade_status();
        if (trade_status.equals("TRADE_SUCCESS")){
            //更新支付记录表的状态为支付成功
            payRecord.setStatus("601002");
            //更新支付宝的订单号
            payRecord.setOutPayNo(payStatusDto.getTrade_no());
            //更新第三方支付渠道编号
            payRecord.setOutPayChannel("AliPay");
            //更新支付成功的时间
            payRecord.setPaySuccessTime(LocalDateTime.now());
            payRecordMapper.updateById(payRecord);

            //更新订单的状态为成功
            orders.setStatus("600002");
            ordersMapper.updateById(orders);

            //将消息写到数据库
            MqMessage message = mqMessageService.addMessage("payresult_notify", orders.getOutBusinessId(), orders.getOrderType(), null);
            //发送消息
            notifyPayResult(message);
        }

    }

    @Override
    public void notifyPayResult(MqMessage message) {
        //消息内容
        String jsonString = JSON.toJSONString(message);

        //创建一个持久化消息
        Message messageObj = MessageBuilder.withBody(jsonString.getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();


        //消息id
        Long id = message.getId();
        //全局消息id
        CorrelationData correlationData = new CorrelationData(id.toString());
        //使用correlationData指定回调方法
        correlationData.getFuture().addCallback(result ->{
            if (result.isAck()){
                //消息成功发送到了交换机
                log.debug("发送消息成功：{}",jsonString);
                //将消息从数据库表mq_message删除
                mqMessageService.completed(id);
            }else{
                //消息发送失败
                log.debug("发送消息失败：{}",jsonString);
            }
        },ex->{
            //发生异常了（可能是网络问题）
            log.debug("mq发消息发生了异常：{}",jsonString);
        });
        //发送消息
        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT,"",messageObj,correlationData);
    }

    /**
     * 请求支付宝查询支付结果
     * @param payNo 支付交易号
     * @return 支付结果
     */
    public PayStatusDto queryPayResultFromAlipay(String payNo){
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, APP_ID, APP_PRIVATE_KEY, "json", AlipayConfig.CHARSET, ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        //bizContent.put("trade_no", "2014112611001004680073956707");
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        String resultJson=null;
        try {
            response = alipayClient.execute(request);
            resultJson= response.getBody();
        } catch (AlipayApiException e) {
            XingZhePlusException.cast("请求支付宝查询支付结果异常");
        }
        Map resultMap = JSON.parseObject(resultJson, Map.class);
        Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response");
        //解析支付结果
        PayStatusDto payStatusDto = new PayStatusDto();
        payStatusDto.setOut_trade_no(payNo);
        payStatusDto.setTrade_no((String) alipay_trade_query_response.get("trade_no"));//支付宝的交易号
        payStatusDto.setTrade_status((String) alipay_trade_query_response.get("trade_status"));//交易状态
        payStatusDto.setApp_id(APP_ID);
        payStatusDto.setTotal_amount((String) alipay_trade_query_response.get("total_amount"));//总金额
        return payStatusDto;
    }


    /**
     * 添加商品订单
     *
     * @param userId
     * @param addOrderDto
     * @return
     */
    public XcOrders saveXcOrders(String userId, AddOrderDto addOrderDto) {
        //进行幂等性判断，同一个选课记录只能有一个订单
        XcOrders orders = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if (orders!=null){
            return orders;
        }

        //添加商品订单
        //插入订单表、订单明细表，支付记录表
        //插入订单主表
        orders = new XcOrders();
        //使用雪花算法生成订单号
        orders.setId(IdWorkerUtils.getInstance().nextId());
        orders.setTotalPrice(addOrderDto.getTotalPrice());
        orders.setCreateDate(LocalDateTime.now());
        orders.setStatus("600001");//未支付
        orders.setUserId(userId);
        orders.setOrderType("60201");//订单类型
        orders.setOrderName(addOrderDto.getOrderName());
        orders.setOrderDescrip(addOrderDto.getOrderDescrip());
        orders.setOrderDetail(addOrderDto.getOrderDetail());
        orders.setOutBusinessId(addOrderDto.getOutBusinessId());//如果是选课这里记录选课表的id
        int insert = ordersMapper.insert(orders);
        if (insert<=0){
            XingZhePlusException.cast("添加订单失败");
        }

        //添加订单明细
        //将前端传递的json数据转为对象
        String orderDetail = addOrderDto.getOrderDetail();
        Long orderId = orders.getId();
        List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetail, XcOrdersGoods.class);
        //遍历xcOrdersGoods插入订单明细表
        xcOrdersGoods.forEach(goods->{
            goods.setOrderId(orderId);
            ordersGoodsMapper.insert(goods);
        });
        return orders;
    }


    /**
     * 保存支付记录
     * @param orders
     * @return
     */
    public XcPayRecord createXcPayRecord(XcOrders orders){
        //订单id
        Long ordersId = orders.getId();
        XcOrders xcOrders = ordersMapper.selectById(ordersId);
        //如果此订单不存在则不能添加支付记录
        if (xcOrders==null){
            XingZhePlusException.cast("订单不存在");
        }
        //订单状态
        String status = orders.getStatus();
        //如果此订单支付结果为成功，则不再添加支付记录，避免重复支付
        if ("601002".equals(status)){
            XingZhePlusException.cast("此订单已经支付啦");
        }

        //添加支付记录
        XcPayRecord xcPayRecord = new XcPayRecord();
        xcPayRecord.setPayNo(IdWorkerUtils.getInstance().nextId());//支付记录号，要传给支付宝的
        xcPayRecord.setOrderId(ordersId);
        xcPayRecord.setOrderName(xcOrders.getOrderName());
        xcPayRecord.setTotalPrice(xcOrders.getTotalPrice());
        xcPayRecord.setCurrency("CNY");
        xcPayRecord.setCreateDate(LocalDateTime.now());
        xcPayRecord.setStatus("601001");//未支付
        xcPayRecord.setUserId(xcOrders.getUserId());
        int insert = payRecordMapper.insert(xcPayRecord);
        if (insert<=0){
            XingZhePlusException.cast("插入支付记录失败");
        }
        return xcPayRecord;
    }

    /**
     * 根据业务id查询订单
     * @param businessId
     * @return
     */
    public XcOrders getOrderByBusinessId(String businessId) {
        XcOrders orders = ordersMapper.selectOne(new LambdaQueryWrapper<XcOrders>().eq(XcOrders::getOutBusinessId, businessId));
        return orders;
    }
}
